package com.mundus.engine;

import org.lwjgl.*;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.*;

import com.mundus.engine.assets.DateTime;
import com.mundus.engine.assets.Log;
import com.mundus.engine.assets.Log.LogType;
import com.mundus.engine.resources.Resource;

public abstract class Engine {
	
	private String _displayTitle;
	private int _displayWidth;
	private int _displayHeight;
	
	public Engine(String title){
		
		Resource.initResource();
		Settings.initSettings();	
		
		Log.applyLogFilter(LogType.NONE);		
		
		this._displayHeight = Settings.getResolution().getHeight();
		this._displayWidth = Settings.getResolution().getWidth();
		this._displayTitle = title;
		
		try{			
			Display.setDisplayMode(Settings.getMatchingDisplayMode());
			Display.setTitle(String.format("%s %sx%s ", title, Settings.getResolution().getWidth(), Settings.getResolution().getHeight()));
			Display.setFullscreen(Settings.getFullscreen());
			
			if(Settings.getFullscreen())
				Display.setVSyncEnabled(Settings.getVsync());
			
			Display.create();
		}
		catch(LWJGLException ex){
			Log.error(ex);
			System.exit(0);
		}
		
		Log.info("OpenGL Version: " + GL11.glGetString(GL11.GL_VERSION));
		GL11.glClearColor(0.4f, 0.6f, 0.9f, 0f);
		GL11.glViewport(0, 0, Settings.getResolution().getWidth(), Settings.getResolution().getHeight());
	}
	
	public void run(){
		try{
			init();
			DateTime.getDelta();
			DateTime.initFPS();
			
			while(!Display.isCloseRequested()){
				update(DateTime.getDelta());
				render(DateTime.getDelta());
				
				if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
					System.exit(0);
				
				Display.sync(Settings.getFPSLimit());
				Display.update();
				DateTime.updateFPS();
								
				if (GL11.glGetError() == GL11.GL_NO_ERROR)
					throw new Exception("GL Error: " + GL11.glGetError());
				
				Display.setTitle(String.format("%s %sx%s FPS:%s", _displayTitle, _displayWidth, _displayHeight, DateTime.getFPS()));
			}
		}
		catch(Exception e){
			Log.error(e);			
		}
		finally{
			Display.destroy();
			destroy();
		}
	}
	
	public abstract void init() throws Exception;
	public abstract void update(int delta) throws Exception;
	public abstract void render(int delta) throws Exception;
	public abstract void destroy();
}
